/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Initial Developer of the Original Code is
*   Nick Kreeger
* Portions created by the Initial Developer are Copyright (C) 2006-2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*   Nick Kreeger <nick.kreeger@park.edu>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */

#import "MessageListDataSource.h"

#import "MailWindowController.h"
#import "MessagesDataSource.h"
#import "MessageHeader.h"
#import "MailboxItem.h"
#import "MessageAccountManager.h"

#import "nsIMsgIncomingServer.h"
#import "nsIMsgFolder.h"
#import "nsIMsgWindow.h"
#import "nsIMsgMailSession.h"
#import "nsMsgBaseCID.h"
#import "nsIMsgHdr.h"
#import "nsISimpleEnumerator.h"
#import "nsIMsgCopyServiceListener.h"
#import "nsISupports.h"
#import "nsIEventQueue.h"
#import "nsIEventQueueService.h"
#import "nsIImapService.h"
#import "nsMsgBaseCID.h"
#import "nsMsgImapCID.h"
#import "nsAutoPtr.h"

#import "nsIMsgImapMailFolder.h"
#import "nsIUrlListener.h"


@interface MessageListDataSource(Private)

-(void)markMessage:(MessageHeader*)inMessageHeader asRead:(BOOL)inIsRead;
-(void)markMessage:(MessageHeader*)inMessageHeader asFlagged:(BOOL)inIsFlagged;

//XXX remove
-(nsISupportsArray*)supportsArrayForMessageIndexSet:(NSIndexSet*)inMsgIndexSet;
-(nsISupportsArray*)supportsArrayForMessageArray:(NSArray*)inMsgArray;

-(nsresult)createSupportsArray:(nsISupportsArray**)inSupportsArray forMessageArray:(NSArray*)inMsgArray;
-(nsresult)createSupportsArray:(nsISupportsArray**)inSupportsArray forIndexSet:(NSIndexSet*)inIndexSet;

-(void)onImapFolderUpdated;

@end


/** 
 * This class simply listens for changes when messages are copied so the data
 * source can be synced real time with the actual storage.
 */
class CHMsgCopyServiceListener : public nsIMsgCopyServiceListener
{
public:
  CHMsgCopyServiceListener();
  virtual ~CHMsgCopyServiceListener();
  
  NS_DECL_ISUPPORTS
  NS_DECL_NSIMSGCOPYSERVICELISTENER
    
  void SetMessageCopyListener(id<MessageCopyListener> aMsgCopyListener);
  
private:
  id<MessageCopyListener> mMessageCopyListener;  // weak ref
};

NS_IMPL_ISUPPORTS1(CHMsgCopyServiceListener, nsIMsgCopyServiceListener);

CHMsgCopyServiceListener::CHMsgCopyServiceListener()
{
  mMessageCopyListener = nil;
}

CHMsgCopyServiceListener::~CHMsgCopyServiceListener()
{
}

void 
CHMsgCopyServiceListener::SetMessageCopyListener(id<MessageCopyListener> aMsgCopyListener)
{
  mMessageCopyListener = aMsgCopyListener;
}

NS_IMETHODIMP 
CHMsgCopyServiceListener::OnStartCopy()
{
#if DEBUG
  NSLog(@"CHMsgCopyServiceListener::OnStartCopy");
#endif
  
  [mMessageCopyListener onStartCopy];
  return NS_OK;
}

NS_IMETHODIMP
CHMsgCopyServiceListener::OnStopCopy(nsresult aStatus)
{
#if DEBUG
  NSLog(@"CHMsgCopyServiceListener::OnStopCopy()");
#endif
  [mMessageCopyListener onStopCopy];
  return NS_OK;
}

NS_IMETHODIMP
CHMsgCopyServiceListener::OnProgress(PRUint32 aProgress, PRUint32 aProgressMax)
{
  return NS_OK;
}

NS_IMETHODIMP
CHMsgCopyServiceListener::SetMessageKey(PRUint32 aKey)
{
  return NS_OK;
}

NS_IMETHODIMP
CHMsgCopyServiceListener::GetMessageId(nsCString* aMessageId)
{
  return NS_OK;
}

/***************************************************************/

/**
 * This class creates and listens to IMAP update folder events
 * and delegates the messages to the cocoa data source.
 *
 * Note that trunk (1.9x branch) contains an extension to
 * |nsIMsgImapMailFolder->UpdateFolder()| that lets us pass
 * in a url listener.
 */
class ImapUpdateListener : public nsIUrlListener
{
public:
  ImapUpdateListener(MessageListDataSource *inMsgListDataSource);
  virtual ~ImapUpdateListener();
  
  NS_DECL_ISUPPORTS
  NS_DECL_NSIURLLISTENER
    
  void UpdateImapFolder(nsIMsgFolder *aMsgFolder);
    
private:
  // If others should listen, let's use a protocol.
  MessageListDataSource *mMsgListDataSource;  // weak ref.
};

NS_IMPL_ISUPPORTS1(ImapUpdateListener, nsIUrlListener);

static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);

ImapUpdateListener::ImapUpdateListener(MessageListDataSource *inMsgListDataSource)
{
  mMsgListDataSource = inMsgListDataSource;
}

ImapUpdateListener::~ImapUpdateListener()
{
}

void ImapUpdateListener::UpdateImapFolder(nsIMsgFolder *aMsgFolder)
{
  nsresult rv;
  nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
  if (!imapService || NS_FAILED(rv))
    return;
  
  // Yuck, create an event queue.
  nsCOMPtr<nsIEventQueue> eventQueue;
  nsCOMPtr<nsIEventQueueService> pEventQService = do_GetService(kEventQueueServiceCID, &rv); 
  if (NS_SUCCEEDED(rv) && pEventQService)
    pEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(eventQueue));
  
  nsCOMPtr<nsIMsgWindow> msgWindow = [[MessageWindowService sharedInstance] getTopmostMsgWindow];
  nsCOMPtr<nsIURI> url;
  imapService->SelectFolder(eventQueue, aMsgFolder, this, msgWindow, getter_AddRefs(url));
}

NS_IMETHODIMP ImapUpdateListener::OnStartRunningUrl(nsIURI *url)
{
  return NS_OK;
}

NS_IMETHODIMP ImapUpdateListener::OnStopRunningUrl(nsIURI *url, nsresult aExitCode)
{
  [mMsgListDataSource onImapFolderUpdated];
  return NS_OK;
}

/***************************************************************/

@implementation MessageListDataSource

-(id)initWithMailWindowController:(MailWindowController*)aMailWindowController
{
  if ((self = [super init]))
  {
    mMailWindowController = aMailWindowController;
    mMessagesDataSource = [[MessagesDataSource alloc] init];
    mCurMailboxItem = nil;
    
    // Load images that are to be used in the message table view
    mFlagImage = [NSImage imageNamed:@"flaggedmail.png"];
    mUnreadImage = [NSImage imageNamed:@"unreadmail.png"];
    mBlankImage = [[NSImage alloc] init];
    
    mSelectedMessageIndex = -1;
    mNextSelectionIndex = -1;
    mIsUpdatingImapFolder = NO;
  }
  
  return self;
}

-(void)dealloc
{
  [mMessagesDataSource release];
  [super dealloc];
}

-(void)didSelectMessageRows:(NSIndexSet*)aIndexSet
{
  if ([aIndexSet count] == 0)
    return;
  
  unsigned int selectedIndex = [aIndexSet firstIndex];
  
  MessageHeader* curMessageHeader = [mMessagesDataSource messageHeaderAtIndex:selectedIndex]; 
  
  // Move this to a timer as necessary.
  // If we want to start the timer once the message has been loaded, move the read flag
  // setting to |MessageWindowService|, since |CHMsgService| tells us once a message loads.
  if (![curMessageHeader isRead])
    [self markMessage:curMessageHeader asRead:YES];
  
  NSString* selectedMsgUri = [self generateUriForMessageHeader:curMessageHeader];
  [[MessageWindowService sharedInstance] loadMessageUri:selectedMsgUri];
  
  mSelectedMessageIndex = selectedIndex;
}

-(void)reloadCurrentMessage
{
  if (mSelectedMessageIndex == -1)
    return;
  
  MessageHeader* curMessageHeader = [mMessagesDataSource messageHeaderAtIndex:mSelectedMessageIndex];
  NSString* selectedMsgUri = [self generateUriForMessageHeader:curMessageHeader];
  [[MessageWindowService sharedInstance] loadMessageUri:selectedMsgUri];
}

-(void)loadMessagesForMailbox:(MailboxItem*)inMailboxItem isUpdating:(BOOL)inIsUpdating
{
  if (!inIsUpdating && inMailboxItem  == mCurMailboxItem)
    return;
  
  if (mCurMailboxItem != inMailboxItem)
  {
    mCurMailboxItem = inMailboxItem;
    mSelectedMessageIndex = -1;
  }
  
  if ([mCurMailboxItem isIMAPFolder] && !mIsUpdatingImapFolder)
  {
    // Since IMAP folder updating requires a URL to be processed, we have to wait until the folder's URL
    // is run. Create our special IMAP update listening class, it will tell us when it is safe to update.
    nsRefPtr<ImapUpdateListener> imapUpdateListener = new ImapUpdateListener(self);
    mIsUpdatingImapFolder = YES;
    imapUpdateListener->UpdateImapFolder([mCurMailboxItem representedMsgFolder]);
  }
  else
  {
    nsresult rv;
    nsCOMPtr<nsIMsgWindow> msgWindow = [[MessageWindowService sharedInstance] getTopmostMsgWindow];
    nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
    if (!msgFolder)
      return;
    
    NSString* selectedMessageId = nil;
    
    // If this event happens, its because the last item in the list was deleted, back track once more.
    if (mSelectedMessageIndex != 0 && ((unsigned int)mSelectedMessageIndex) == [mMessagesDataSource messageCount])
      mSelectedMessageIndex -= 1;
    
    // Save the selected message's ID so we can reselect it after we attempt to update the folder.
    if (mSelectedMessageIndex != -1 && [mMessagesDataSource messageCount] > 0)
      selectedMessageId = [[mMessagesDataSource messageHeaderAtIndex:mSelectedMessageIndex] messageId];
    
    msgFolder->StartFolderLoading();
    
    nsCOMPtr<nsISimpleEnumerator> messagesEnum;
    rv = msgFolder->GetMessages(msgWindow, getter_AddRefs(messagesEnum));
    if (NS_SUCCEEDED(rv) && messagesEnum)
      [mMessagesDataSource createMessageListForHeaderEnum:messagesEnum];
    
    [mMailWindowController refreshMessageList];
    
    // Ugh, iterate through the list.
    if (selectedMessageId)
    {
      for (unsigned int i = 0; i < [mMessagesDataSource messageCount]; i++)
      {
        if ([[[mMessagesDataSource messageHeaderAtIndex:i] messageId] isEqualToString:selectedMessageId])
        {
          [mMailWindowController scrollMessagesToSelectRow:i];
          break;
        }
      }
    }
    
    msgFolder->EndFolderLoading();
  }
}

-(void)clearMessages
{
  [mMessagesDataSource clearMessages];
  mCurMailboxItem = nil;
}

-(void)sortUsingSelector:(SEL)aSelector
{
  // Save the |MessageHeader| pointer to the current selected message so we can re-select it
  // after the sort.
  MessageHeader* selectedMessageHeader = nil;
  if (mSelectedMessageIndex != -1)
    selectedMessageHeader = [mMessagesDataSource messageHeaderAtIndex:mSelectedMessageIndex];
  
  [mMessagesDataSource sortUsingSelector:aSelector];
  
  if (selectedMessageHeader)
  {
    int selectionIndex = [mMessagesDataSource indexOfMessageHeader:selectedMessageHeader];
    if (selectionIndex > 0)
      [mMailWindowController scrollMessagesToSelectRow:selectionIndex];
  }
}

-(BOOL)isSortedDescending
{
  return [mMessagesDataSource isSortedDescending];
}

-(unsigned int)messageCount
{
  return [mMessagesDataSource messageCount];
}

-(unsigned int)unreadMessageCount
{
  // It's easiest to ask the |nsIMsgFolder| about this.
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return 0;
  
  PRInt32 numUnread = 0;
  msgFolder->GetNumUnread(PR_FALSE, &numUnread);
  
  return numUnread;
}

-(unsigned int)newMessageCount
{
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return 0;
  
  PRInt32 numNew = 0;
  msgFolder->GetNumNewMessages(PR_FALSE, &numNew);
    
  return numNew;
}

-(BOOL)isCurrentMailboxIMAP
{
  if (!mCurMailboxItem)
    return NO;
  
  return [mCurMailboxItem isIMAPFolder];
}

-(void)markMessageIndexes:(NSIndexSet*)inMessagesIndexes areRead:(BOOL)inIsRead
{
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return;
  
  nsCOMPtr<nsISupportsArray> msgArray;
  if (NS_SUCCEEDED([self createSupportsArray:getter_AddRefs(msgArray) forIndexSet:inMessagesIndexes]))
  {
    msgFolder->MarkMessagesRead(msgArray, inIsRead);
    [mMailWindowController refreshMessageList];
  }
}

-(void)markMessageIndexes:(NSIndexSet*)inMessagesIndexes areFlagged:(BOOL)inIsFlagged
{
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return;
  
  nsCOMPtr<nsISupportsArray> msgArray;
  if (NS_SUCCEEDED([self createSupportsArray:getter_AddRefs(msgArray) forIndexSet:inMessagesIndexes]))
  {
    msgFolder->MarkMessagesFlagged(msgArray, inIsFlagged);
    [mMailWindowController refreshMessageList];
  }
}

-(void)markMessage:(MessageHeader*)inMessageHeader asRead:(BOOL)inIsRead
{
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return;
  
  nsCOMPtr<nsISupportsArray> msgArray;
  if (NS_SUCCEEDED([self createSupportsArray:getter_AddRefs(msgArray) 
                             forMessageArray:[NSArray arrayWithObject:inMessageHeader]]))
  {
    msgFolder->MarkMessagesRead(msgArray, inIsRead);
    [mMailWindowController refreshMessageList];
  }
}

-(void)markMessage:(MessageHeader*)inMessageHeader asFlagged:(BOOL)inIsFlagged
{
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return;
  
  nsCOMPtr<nsISupportsArray> msgArray;
  if (NS_SUCCEEDED([self createSupportsArray:getter_AddRefs(msgArray)
                             forMessageArray:[NSArray arrayWithObject:inMessageHeader]]))
  {
    msgFolder->MarkMessagesFlagged(msgArray, inIsFlagged);
    [mMailWindowController refreshMessageList];
  }
}

-(void)markAllMessagesRead
{
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return;
  
  msgFolder->MarkAllMessagesRead();
}

-(void)deleteMessages:(NSIndexSet*)inMsgIndexSet
{
  if (!mCurMailboxItem)
    return;
  
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return;
  
  // First, make sure we can delete the messages
  nsresult rv;
  PRBool canDelete = PR_FALSE;
  rv = msgFolder->GetCanDeleteMessages(&canDelete);
  if (NS_FAILED(rv) || !canDelete)
    return;
  

  nsCOMPtr<nsIMsgWindow> msgWindow = [[MessageWindowService sharedInstance] getTopmostMsgWindow];
  if (!msgWindow)
  {
    NSLog(@"ERROR: Could not get a message window");
    return;
  }
  
  // Keep this interface for now, it doesn't do anything ATM. 
  // Could be that the folder listener already keeps track of any changes like this.
  // We will need this to notify us when something has been deleted, so we can update
  // the message display.
  CHMsgCopyServiceListener* copyListener = new CHMsgCopyServiceListener();
  copyListener->SetMessageCopyListener(self);
  
  nsCOMPtr<nsISupportsArray> msgArray;
  if (NS_SUCCEEDED([self createSupportsArray:getter_AddRefs(msgArray) forIndexSet:inMsgIndexSet]))
  {
    // Try to set our next pivot index to |n - 1|, if that is not available, select |n|.
    // If there is only one message in the list, the selection index will be -1 which
    // will cause |onStopCopy:| to not tell MWC to select messages that don't exist.
    mNextSelectionIndex = [inMsgIndexSet firstIndex];
    if (mNextSelectionIndex > 0 || [mMessagesDataSource messageCount] == 1)
      mNextSelectionIndex = mNextSelectionIndex - 1;
    
    msgFolder->DeleteMessages(msgArray,
                              msgWindow,
                              PR_FALSE,      // delete storage : [self isCurrentMailboxIMAP]
                              PR_FALSE,      // is move
                              copyListener,  // nsIMsgCopyServiceListener
                              PR_TRUE);      // allow undo
  }
}

-(nsISupportsArray*)supportsArrayForMessageIndexSet:(NSIndexSet*)inMsgIndexSet
{
  if (!inMsgIndexSet)
    return nsnull;
  
  nsresult rv;
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return nsnull;
  
  nsCOMPtr<nsISupportsArray> msgArray;
  rv = NS_NewISupportsArray(getter_AddRefs(msgArray));
  if (NS_FAILED(rv))
    return nsnull;
  
  unsigned int curIndex = [inMsgIndexSet firstIndex];
  while (curIndex != NSNotFound)
  {
    MessageHeader* curMessageHeader = [mMessagesDataSource messageHeaderAtIndex:curIndex];
    if (curMessageHeader)
      msgArray->AppendElement([curMessageHeader representedMsgHdr]);
    
    curIndex = [inMsgIndexSet indexGreaterThanIndex:curIndex];
  }

  return msgArray;
}

-(nsresult)createSupportsArray:(nsISupportsArray**)inSupportsArray forMessageArray:(NSArray*)inMsgArray
{
  if (!inMsgArray)
    return NS_ERROR_FAILURE;
  
  NS_ENSURE_ARG_POINTER(inSupportsArray);
  
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return NS_ERROR_FAILURE;
  
  nsresult rv;
  nsCOMPtr<nsISupportsArray> msgArray;
  rv = NS_NewISupportsArray(getter_AddRefs(msgArray));
  NS_ENSURE_SUCCESS(rv, rv);
  
  MessageHeader* curMessageHeader = nil;
  NSEnumerator* msgEnum = [inMsgArray objectEnumerator];
  while ((curMessageHeader = [msgEnum nextObject]))
    msgArray->AppendElement([curMessageHeader representedMsgHdr]);
  
  NS_ADDREF(*inSupportsArray = msgArray);
  return NS_OK;
}

-(nsresult)createSupportsArray:(nsISupportsArray**)inSupportsArray forIndexSet:(NSIndexSet*)inIndexSet
{
  if (!inIndexSet)
    return NS_ERROR_FAILURE;
  
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return NS_ERROR_FAILURE;
  
  nsresult rv;
  nsCOMPtr<nsISupportsArray> msgArray;
  rv = NS_NewISupportsArray(getter_AddRefs(msgArray));
  NS_ENSURE_SUCCESS(rv, rv);
  
  unsigned int curIndex = [inIndexSet firstIndex];
  while (curIndex != NSNotFound)
  {
    MessageHeader* curMessageHeader = [mMessagesDataSource messageHeaderAtIndex:curIndex];
    if (curMessageHeader)
      msgArray->AppendElement([curMessageHeader representedMsgHdr]);
    
    curIndex = [inIndexSet indexGreaterThanIndex:curIndex];
  }
  
  NS_ADDREF(*inSupportsArray = msgArray);
  
  return NS_OK;
}

// This needs to be a double pointer so it can be add-reffed.
-(nsISupportsArray*)supportsArrayForMessageArray:(NSArray*)inMsgArray
{
  if (!inMsgArray)
    return nsnull;
  
  nsresult rv;
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return nsnull;
  
  nsCOMPtr<nsISupportsArray> msgArray;
  rv = NS_NewISupportsArray(getter_AddRefs(msgArray));
  if (NS_FAILED(rv))
    return nsnull;
  
  MessageHeader* curMessageHeader = nil;
  NSEnumerator* msgEnum = [inMsgArray objectEnumerator];
  while ((curMessageHeader = [msgEnum nextObject]))
    msgArray->AppendElement([curMessageHeader representedMsgHdr]);
  
  return msgArray;
}

-(NSString*)generateUriForMessageHeader:(MessageHeader*)aMessageHeader
{
  nsresult rv;
  nsIMsgDBHdr* msgDBHdr = [aMessageHeader representedMsgHdr];
  
  nsMsgKey msgKey;
  rv = msgDBHdr->GetMessageKey(&msgKey);
  NS_ENSURE_SUCCESS(rv, nil);
  
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return nil;
  
  nsXPIDLCString uri;
  rv = msgFolder->GenerateMessageURI(msgKey, getter_Copies(uri));
  if (NS_FAILED(rv) || !uri)
    return nil;
  
  return [[NSString stringWithUTF8String:uri] autorelease];
}

-(NSString*)generateUriForMessageAtIndex:(int)inIndex
{
  MessageHeader* messageHeader = [mMessagesDataSource messageHeaderAtIndex:inIndex];
  if (!messageHeader)
    return nil;
  
  nsresult rv;
  nsIMsgDBHdr* msgDBHdr = [messageHeader representedMsgHdr];
  
  nsMsgKey msgKey;
  rv = msgDBHdr->GetMessageKey(&msgKey);
  NS_ENSURE_SUCCESS(rv, nil);
  
  nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
  if (!msgFolder)
    return nil;
  
  nsXPIDLCString uri;
  rv = msgFolder->GenerateMessageURI(msgKey, getter_Copies(uri));
  if (NS_FAILED(rv) || !uri)
    return nil;
  
  return [[NSString stringWithUTF8String:uri] autorelease];
}

-(NSString*)messageIdForMessageAtIndex:(int)inIndex
{
  MessageHeader* messageHeader = [mMessagesDataSource messageHeaderAtIndex:inIndex];
  if (!messageHeader)
    return nil;
  
  return [messageHeader messageId];
}

-(unsigned int)messageIndexForMessageId:(NSString*)aMessageID
{
  return [mMessagesDataSource messageIndexForMessageId:aMessageID];
}

-(MessageHeader*)messageHeaderAtIndex:(int)inIndex
{
  return [mMessagesDataSource messageHeaderAtIndex:inIndex];
}

-(void)onStartCopy
{
  // It would be nice to provide some feedback that the message _is_
  // getting deleted here.
}

-(void)onStopCopy
{
  if (mNextSelectionIndex == -1)
    return;
    
  if ([self isCurrentMailboxIMAP])
  {
    nsIMsgFolder* msgFolder = [mCurMailboxItem representedMsgFolder];
    nsCOMPtr<nsIMsgWindow> msgWindow = [[MessageWindowService sharedInstance] getTopmostMsgWindow];
    msgFolder->UpdateFolder(msgWindow);
  }
  
  [self loadMessagesForMailbox:mCurMailboxItem isUpdating:YES];  // problem is here
  
  // paranoia, make sure the message list is fresh
  [mMailWindowController refreshMessageList];
  
  // The copy has ended, let's select our new message set.
  [mMailWindowController selectMessageIndexes:[NSIndexSet indexSetWithIndex:mNextSelectionIndex]];
}

-(void)onImapFolderUpdated
{
  // Ok, now we can actually update the IMAP folder... phew.
  [self loadMessagesForMailbox:mCurMailboxItem isUpdating:YES];
  mIsUpdatingImapFolder = NO;
}

#pragma mark -

//
// NSTableDataSource Protocol Methods
// 
- (id)tableView:(NSTableView*)inTableView
      objectValueForTableColumn:(NSTableColumn*)inTableColumn
            row:(int)inRowIndex
{
  NSString* identifier = [inTableColumn identifier];
  MessageHeader* curMessageHeader = [mMessagesDataSource messageHeaderAtIndex:inRowIndex];
  
  if ([identifier isEqualToString:@"isFlagged"])
  {
    if ([curMessageHeader isFlagged])
      return mFlagImage;
    else
      return mBlankImage;
  }
  else if ([identifier isEqualToString:@"isRead"])
  {
    if ([curMessageHeader isRead])
      return mBlankImage;
    else
      return mUnreadImage; 
  }
  
  return [curMessageHeader valueForKey:identifier];
}

-(int)numberOfRowsInTableView:(NSTableView*)inTableView
{
  return [mMessagesDataSource messageCount];
}

@end
